import arrayPrototype from "./array";
import Dep from "./dep";

export function observe(data) {
    if(typeof data !== 'object' || data == null){
        return data; //如果不是对象类型，那么不要做任何处理
    }

    //稍后要区分 如果一个对象已经被观测过了，就不要再被观测了
    // __ob__表示是否被观测过
    if(data.__ob__){
        return data
    }

    return new Observer(data);

    
}

//高内聚 低耦合
class Observer{
    constructor(data){
        //给每个对象都增加收集功能
        this.dep = new Dep(); //所有对象都要增加

        //如果是数组的话也是用defineProperty会浪费很多性能 很少用户会通过arr[878]=123
        //vue3中的polyfill 直接就给数组做代理了
        //改写数组的方法，如果用户调用了可以改写数组方法的api 那么我就去劫持这个方法
        //变异方法 push pop shift unshift reverse sort splice
        
        // data.__ob__=this; //如果__ob__属性 说明被观测过
        //改写为：
        Object.defineProperty(data,'__ob__',{
            value:this,
            enumerable:false
        })

        //修复数组的索引和长度是无法更新视图的
        if(Array.isArray(data)){
            //需要重写这7个方法
            data.__proto__=arrayPrototype;

            //直接将属性赋值给这个对象
            //如果数组里面放的是对象类型 也希望变成响应式
            this.observeArray(data);
        } else{
            this.walk(data)
        }
    }
    observeArray(data){
        data.forEach(item=>observe(item));
    }
    walk(data){
        //循环对象 不用for in(会遍历原型链)
        let keys = Object.keys(data);
        keys.forEach(key=>{//没有重写数组里的每一项
            //没有重写数组里的每一项
            defineReactive(data,key,data[key]);
        })
    }
}

//深层次嵌套会递归，递归多了性能差,不存在属性监控不到，存在的属性重写方法 vue3->proxy
function dependArray(value){
    for(let i=0;i<value.length;i++){
        let current = value[i];
        if(current.__ob__ && current.__ob__.dep){
            current.__ob__.dep.depend();
            if(Array.isArray(current)){
                dependArray(current);
            }
        }
    }
}

//性能不好的原因在于 所有的属性都被重新定义了一遍
//一上来需要将对象深度代理，性能差
function defineReactive(data,key,value){//闭包
    //属性会全部被重写增加get和set

    let childOb = observe(value);//递归代理属性 childOb.dep用来手机依赖的

    let dep = new Dep();//每一个属性都有一个dep
    Object.defineProperty(data,key,{
        get(){ //取值的时候 会执行get
            if(Dep.target){
                dep.depend();

                if(childOb && childOb.dep){
                    childOb.dep.depend();//让数组和对象本身也实现依赖收集

                    if(Array.isArray(value)){
                        dependArray(value);
                    }
                }

            }
            return value;
        },
        set(newValue){ //vm.xxx = {a:1}
            observe(newValue);
            if(newValue!==value){
                value = newValue;
            }
            dep.notify();
        }
    })
}